import { db } from './db';

interface CollectionReference<T> {
	/**
	 * 获取集合中指定记录的引用
	 * @param docID 要引用的文档的_id
	 * @returns 文档引用对象
	 */
	doc(docID: string): CollectionDocument<T>;
	/**
	 * 向集合添加新文档。如果未指定_id则自动生成。
	 * 如果指定了_id，则不能与现有文档冲突
	 * @param data 要添加的文档数据
	 * @returns 返回添加的文档Promise
	 */
	add(data: Partial<T>): Promise<UniCloudAddData>;
	/**
	 * 从集合中检索文档，可选择通过查询参数过滤
	 * @param param 可选的查询参数
	 * @returns 返回查询结果Promise
	 */
	get(param?: UniCloud.GetParam): Promise<UniCloudGetData<T>>;
	getOne(): Promise<UniCloudGetOneData<T>>;
	getTree(param?: UniCloud.GetTreeParam): Promise<UniCloudGetTreeData<T>>;
	/**
	 * 创建一个可以稍后执行的延迟查询
	 * @param param 可选的查询参数
	 * @returns 临时查询对象
	 */
	getTemp(param?: UniCloud.GetParam): UniCloud.GetTempQuery;
	/**
	 * 统计符合查询条件的文档数量
	 * @returns 返回计数Promise
	 */
	count(): Promise<UniClouldCountData>;
	/**
	 * 应用查询条件以过滤文档
	 * @param query 查询条件
	 * @returns 应用了条件的新集合查询
	 */
	where(query: Partial<T> | Partial<Record<keyof T, UniCloud.LogicCommand | RegExp | void | any>> | string): CollectionQuery<T>;
	/**
	 * 指定查询结果的排序方式
	 * @param fieldPath 要排序的字段
	 * @param directionStr 排序方向('asc'升序或'desc'降序)
	 * @returns 应用了排序的新集合查询
	 */
	orderBy(fieldPath: keyof T | string.DBFieldString, directionStr: 'desc' | 'asc'): CollectionQuery<T>;
	/**
	 * 限制返回结果的数量
	 * @param limit 最大返回文档数
	 * @returns 应用了限制的新集合查询
	 */
	limit(limit: number): CollectionQuery<T>;
	/**
	 * 跳过指定数量的结果（用于分页）
	 * @param offset 要跳过的文档数
	 * @returns 应用了跳过的新集合查询
	 */
	skip(offset: number): CollectionQuery<T>;
	/**
	 * 更新符合查询条件的多个文档
	 * @param data 要应用的更新数据
	 * @returns 返回更新结果Promise
	 */
	update(data: Partial<T>): Promise<UniCloudUpdateData>;
	/**
	 * 指定结果中要包含的字段
	 * @param projection 要包含的字段
	 * @returns 应用了字段投影的新集合查询
	 */
	field(projection: Partial<Record<keyof T, boolean>> | string.DBFieldString): CollectionQuery<T>;
	/**
	 * 指定要使用的外键关系
	 * @param foreignKeyName 外键名称
	 * @returns 指定了外键的新集合查询
	 */
	foreignKey(foreignKeyName: string): CollectionQuery<T>;
	/**
	 * 指定分组前要包含的字段
	 * @param projection 要包含的字段
	 * @returns 指定了预分组字段的新集合查询
	 */
	preField(projection: string): CollectionQuery<T>;
	/**
	 * 按指定字段对结果分组
	 * @param projection 要分组的字段
	 * @returns 应用了分组的新集合查询
	 */
	groupBy(projection: string): CollectionQuery<T>;
	/**
	 * 指定分组结果中要包含的字段
	 * @param projection 分组中要包含的字段
	 * @returns 指定了分组字段的新集合查询
	 */
	groupField(projection: string): CollectionQuery<T>;
	/**
	 * 删除符合查询条件的文档
	 * 注意：仅支持通过where条件删除，不支持skip和limit
	 * @returns 返回删除结果Promise
	 */
	remove(): Promise<any>;
	/**
	 * 获取集合的聚合管道
	 * @returns 用于构建聚合操作的聚合引用
	 */
	aggregate(): UniCloud.AggregateReference;
}

interface CollectionDocument<T> {
	/**
	 * 更新一条记录
	 */
	update(data: Partial<T>): Promise<UniCloudUpdateData>;
	/**
	 * 删除一条记录
	 */
	remove(): Promise<any>;
	/**
	 * 获取记录数据
	 */
	get(GetParam?: UniCloud.GetParam): Promise<UniCloudGetData<T>>;
	getOne(): Promise<UniCloudGetOneData<T>>;
	getTree(param?: UniCloud.GetTreeParam): Promise<UniCloudGetTreeData<T>>;
	/**
	 * 获取记录数据(延迟查询)
	 */
	getTemp(GetParam?: UniCloud.GetParam): UniCloud.GetTempQuery;
	/**
	 * 指定返回结果中记录需返回的字段
	 */
	field(fields: string | string.DBFieldString): CollectionDocument<T>;
	/**
	 * 指定要使用的foreignKey
	 */
	foreignKey(foreignKeyName: string): CollectionDocument<T>;
	/**
	 * 指定返回结果中记录需返回的字段
	 */
	preField(fields: string | string.DBFieldString): CollectionQuery<T>;
	/**
	 * 指定分组依据
	 */
	groupBy(field: string | string.DBFieldString): CollectionQuery<T>;
	/**
	 * 指定统计指标
	 */
	groupField(fields: string | string.DBFieldString): CollectionQuery<T>;
}

interface CollectionQuery<T> {
	/**
	 * 获取记录数据，或获取根据查询条件筛选后的记录数据
	 */
	get(param?: UniCloud.GetParam): Promise<UniCloudGetData<T>>;
	getOne(): Promise<UniCloudGetOneData<T>>;
	getTree(param?: UniCloud.GetTreeParam): Promise<UniCloudGetTreeData<T>>;

	/**
	 * 延迟查询请求
	 */
	getTemp(param?: UniCloud.GetParam): UniCloud.GetTempQuery;

	/**
	 * 统计匹配查询条件的记录的条数
	 */
	count(): Promise<UniClouldCountData>;

	/**
	 * 指定查询条件，返回带新查询条件的新的集合引用
	 */
	where(query: Partial<T> | Partial<Record<keyof T, UniCloud.LogicCommand | void>> | string): CollectionQuery<T>;

	/**
	 * 指定查询排序条件
	 */
	orderBy(fieldPath: keyof T | string.DBFieldString, directionStr: 'desc' | 'asc'): CollectionQuery<T>;

	/**
	 * 指定查询结果集数量上限
	 */
	limit(limit: number): CollectionQuery<T>;

	/**
	 * 指定查询返回结果时从指定序列后的结果开始返回，常用于分页
	 */
	skip(offset: number): CollectionQuery<T>;

	/**
	 * 更新多条记录
	 */
	update(data: Partial<T>): Promise<UniCloudUpdateData>;

	/**
	 * 指定返回结果中记录需返回的字段
	 */
	field(projection: Partial<Record<keyof T, boolean>> | string.DBFieldString): CollectionQuery<T>;

	/**
	 * 指定要使用的foreignKey
	 */
	foreignKey(foreignKeyName: string): CollectionQuery<T>;

	/**
	 * 指定分组依据
	 */
	groupBy(field: string | string.DBFieldString): CollectionQuery<T>;

	/**
	 * 指定统计指标
	 */
	groupField(fields: string | string.DBFieldString): CollectionQuery<T>;

	/**
	 * 删除多条记录。注意只支持通过匹配 where 语句来删除，不支持 skip 和 limit
	 */
	remove(): Promise<any>;
}

export const getCollection = <T extends Record<string, any>>(
	...collections: (UniCloud.GetTempQuery | string | string.DBCollectionString)[]
): CollectionReference<T> => {
	const collection = db.collection(...collections);

	const createQuery = (col: UniCloud.Query): CollectionQuery<T> => {
		return {
			where: (condition) => createQuery(col.where(condition)),
			field: (fields) => createQuery(col.field(fields)),
			orderBy: (field, order) => createQuery(col.orderBy(field as string, order)),
			skip: (offset) => createQuery(col.skip(offset)),
			limit: (max) => createQuery(col.limit(max)),
			get: (params) => col.get(params),
			getOne: () => col.get({ getOne: true }),
			getTree: (params) => col.get({ getTree: true, ...params }),
			count: () => col.count(),
			update: (data) => col.update(data),
			remove: () => col.remove(),
			getTemp: (params) => col.getTemp(params),
			foreignKey: (foreignKeyName) => createQuery(col.foreignKey(foreignKeyName)),
			groupBy: (field) => createQuery(col.groupBy(field)),
			groupField: (fields) => createQuery(col.groupField(fields)),
		};
	};

	const createDocument = (doc: UniCloud.DocumentReference): CollectionDocument<T> => {
		return {
			field: (fields) => createDocument(doc.field(fields)),
			foreignKey: (foreignKeyName) => createDocument(doc.foreignKey(foreignKeyName)),
			groupBy: (field) => createQuery(doc.groupBy(field)),
			groupField: (fields) => createQuery(doc.groupField(fields)),
			get: (params) => doc.get(params),
			getOne: () => doc.get({ getOne: true }),
			getTree: (params) => doc.get({ getTree: true, ...params }),
			getTemp: (params) => doc.getTemp(params),
			update: (data) => doc.update(data) as Promise<UniCloudUpdateData>,
			remove: () => doc.remove(),
			preField: (fields) => createQuery(doc.preField(fields)),
		};
	};

	return {
		where: (condition) => createQuery(collection.where(condition)),
		get: (params) => collection.get(params),
		getOne: () => collection.get({ getOne: true }),
		getTree: (params) => collection.get({ getTree: true, ...params }),
		add: (data: T) => collection.add(data) as Promise<UniCloudAddData>,
		doc: (id: string) => createDocument(collection.doc(id)),
		getTemp: () => collection.getTemp(),
		aggregate: () => collection.aggregate(),
		count: () => collection.count(),
		orderBy: (field, order) => createQuery(collection.orderBy(field as string, order)),
		limit: (max) => createQuery(collection.limit(max)),
		skip: (offset) => createQuery(collection.skip(offset)),
		update: (data) => collection.update(data),
		remove: () => collection.remove(),
		field: (fields) => createQuery(collection.field(fields as any)),
		foreignKey: (foreignKeyName) => createQuery(collection.foreignKey(foreignKeyName)),
		preField: (fields) => createQuery(collection.preField(fields)),
		groupBy: (field) => createQuery(collection.groupBy(field)),
		groupField: (fields) => createQuery(collection.groupField(fields)),
	};
};
